// package shell implements a remote API interface for a running ipfs daemon
package shell

import (
	"context"
	"encoding/json"
	"io/ioutil"
	gohttp "net/http"
	"os"
	"path"
	"strings"
	"time"

	homedir "github.com/mitchellh/go-homedir"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

const (
	DefaultPathName   = ".ipfs"
	DefaultPathRoot   = "~/" + DefaultPathName
	DefaultApiFile    = "api"
	DefaultConfigFile = "config"
	EnvDir            = "IPFS_PATH"
)

var shellIdentity IpfsIdentity

type Shell struct {
	url      string
	httpcli *gohttp.Client
	identity IpfsIdentity
}

type IpfsIdentity struct {
	PeerID  string
	PrivKey string
}

type IPFSConfig struct {
	Identity IpfsIdentity
}

func SetIPFSEnv(path string) error {
	return os.Setenv("IPFS_PATH", path)
}

func NewLocalShell() *Shell {
	baseDir := os.Getenv(EnvDir)
	if baseDir == "" {
		baseDir = DefaultPathRoot
	}

	baseDir, err := homedir.Expand(baseDir)
	if err != nil {
		return nil
	}

	apiFile := path.Join(baseDir, DefaultApiFile)

	if _, err := os.Stat(apiFile); err != nil {
		return nil
	}

	api, err := ioutil.ReadFile(apiFile)
	if err != nil {
		return nil
	}

	//private key get
	configFile := path.Join(baseDir, DefaultConfigFile)

	if _, err := os.Stat(configFile); err != nil {
		return nil
	}

	config, err := ioutil.ReadFile(configFile)
	if err != nil {
		return nil
	}

	var ipfsConfig IPFSConfig

	if err := json.Unmarshal(config, &ipfsConfig); err == nil {
		shellIdentity = ipfsConfig.Identity
	}

	return NewShell(strings.TrimSpace(string(api)))
}

func NewShell(url string) *Shell {
	c := &gohttp.Client{
		Transport: &gohttp.Transport{
			DisableKeepAlives: true,
		},
	}

	return NewShellWithClient(url, c)
}

func NewShellWithClient(url string, c *gohttp.Client) *Shell {
	if a, err := ma.NewMultiaddr(url); err == nil {
		_, host, err := manet.DialArgs(a)
		if err == nil {
			url = host
		}
	}

	return &Shell{
		url:      url,
		httpcli: c,
		identity: shellIdentity,
	}
}

func (s *Shell) SetTimeout(d time.Duration) {
	s.httpcli.Timeout = d
}

func (s *Shell) Request(command string, args ...string) *RequestBuilder {
	return &RequestBuilder{
		command: command,
		args:    args,
		shell:   s,
	}
}

func (s *Shell) NewRequest(ctx context.Context, command string, args ...string) *Request {
	return NewRequest(ctx, s.url, command, args...)
}

func (s *Shell) GetPrivateKey(peerid string) string {
	if peerid == s.identity.PeerID {
		return s.identity.PrivKey
	}
	return ""
}
